﻿
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;

using JDI.Common;
using JDI.Common.Logger;
using JDI.Pusher.Client;
using JDI.Common.Utils;

namespace JDI.Pusher.WinFormApp
{
	public partial class MainForm : Form, ILogger
	{
		private FileLogger fileLogger;
        private string pusherAppKey = "";
		private PusherClient pusherClient;
		private PusherChannel pusherChannel;

		public MainForm()
		{
			InitializeComponent();
		}

		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);

            // load pusher App key
            string filePath = "c:\\jdiPusher\\";
            string fileName = "WinFormApp";
            pusherAppKey = null;
            if (this.LoadPusherAppKey(filePath, fileName, out pusherAppKey) == false)
            {
                // failed to load pusher appKey
                this.connectButton.Enabled = false;
                return;
            }

            // init logger
		//	this.fileLogger = new FileLogger();
		//	this.fileLogger.Open(filePath, fileName);
		//	Logger.Initialize(this.fileLogger, LogLevel.Debug);
		//	Logger.Initialize(new DebugLogger(), LogLevel.Info);
			Logger.Initialize(this, LogLevel.Info);

			// init network
			if (this.InitializeNetwork(true) == false)
			{
				// failed to initialize network
                this.connectButton.Enabled = false;
                return;
			}

			// setup options
			PusherOptions options = new PusherOptions();
			options.EncryptionEnabled = true;
			options.MaskingEnabled = true;

			// init pusher client
			this.pusherClient = new PusherClient("Pusher", options);

			// attach event handlers
			this.pusherClient.ConnectionChanged += new PusherDelegates.ConnectionChangedEventHandler(pusherClient_ConnectionChanged);
			this.pusherClient.Connected += new PusherDelegates.ConnectedEventHandler(pusherClient_Connected);
			this.pusherClient.Disconnected += new PusherDelegates.DisconnectedEventHandler(pusherClient_Disconnected);
			this.pusherClient.Error += new PusherDelegates.ErrorEventHandler(pusherClient_Error);

			// monitor all events
			this.pusherClient.BindAll(this.pusherClient_BindAll);

			this.pusherChannel = null;
        }

		protected override void OnFormClosing(FormClosingEventArgs e)
		{
			base.OnFormClosing(e);

            if (this.pusherClient != null)
            {
                this.pusherClient.UnBindAll(this.pusherClient_BindAll);
                this.pusherClient.Disconnect();
                this.pusherClient.Dispose();
            }
            this.pusherClient = null;

			if (this.fileLogger != null)
			{
				this.fileLogger.Close();
				this.fileLogger.Dispose();
			}
			this.fileLogger = null;
		}


        #region Private Methods

        private bool LoadPusherAppKey(string filePath, string fileName, out string pusherAppKey)
        {
            DebugEx.WriteLine("Loading Pusher AppKey...");

            pusherAppKey = null;
            fileName = string.Concat(fileName, ".ini");
            filePath = Path.Combine(filePath, fileName);
            if (File.Exists(filePath))
            {
                StreamReader streamReader = null;
                try
                {
                    using (streamReader = new StreamReader(filePath))
                    {
                        // assume this is the only line in the file
                        string line = streamReader.ReadLine().Trim();

                        string key = null;
                        string value = null;
                        int keyvalueSepPos = line.IndexOf("=");
                        if (keyvalueSepPos > 0)
                        {
                            key = line.Substring(0, keyvalueSepPos).Trim().ToLower();
                            value = line.Substring(keyvalueSepPos + 1).Trim();
                            if (key == "appkey")
                            {
                                pusherAppKey = value;
                                DebugEx.WriteLine("Pusher AppKey loaded.");
                                return true;
                            }
                        }
                    }
                }
                catch
                {
                    // no need to do anything
                }
                finally
                {
                    if (streamReader != null)
                    {
                        streamReader.Close();
                        streamReader = null;
                    }
                }
            }

            DebugEx.WriteLine("Failed to load Pusher AppKey.");
            return false;
        }

        private bool InitializeNetwork(bool useDHCP)
		{
			return true;
		}

		private void UpdateUI(int? pusherState, int? channelState, string message)
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(delegate { this.UpdateUI(pusherState, channelState, message); }));
			}
			else
			{
				if (pusherState != null)
				{
					PusherState ps = (PusherState)pusherState;
					this.statusBarMessage.Text = ps.Name();
					if (ps == PusherState.Connected)
					{
						this.connectButton.Enabled = false;
						this.disconnectButton.Enabled = true;
						this.subscribeButton.Enabled = true;
						this.unsubscribeButton.Enabled = false;
					}
					else if (ps == PusherState.Disconnected)
					{
						this.connectButton.Enabled = true;
						this.disconnectButton.Enabled = false;
						this.subscribeButton.Enabled = false;
						this.unsubscribeButton.Enabled = false;
					}
				}

				if (channelState != null)
				{
					PusherChannelState cs = (PusherChannelState)channelState;
					if (cs == PusherChannelState.Subscribed)
					{
						this.subscribeButton.Enabled = false;
						this.unsubscribeButton.Enabled = true;
					}
					else
					{
						this.subscribeButton.Enabled = true;
						this.unsubscribeButton.Enabled = false;
					}
				}

				if (message != null)
				{
					this.statusBarMessage.Text = message;
				}
			}
		}

        #endregion


        #region Event Handlers

        private void connectButton_Click(object sender, EventArgs e)
		{
			this.pusherClient.Connect(this.pusherAppKey);
		}

		private void disconnectButton_Click(object sender, EventArgs e)
		{
			this.pusherClient.Disconnect();
		}

		private void subscribeButton_Click(object sender, EventArgs e)
		{
			this.pusherChannel = this.pusherClient.Subscribe("testChannel", pusherChannel_StatusChanged);
		}

		private void unsubscribeButton_Click(object sender, EventArgs e)
		{
			this.pusherClient.UnSubscribe("testChannel");
			this.pusherChannel = null;
		}

		private void pusherClient_BindAll(string eventName, string eventData)
		{
			Logger.WriteInfo("Main", "BindAll Event: " + eventName + ", Data: " + eventData);
		}

		private void pusherClient_ConnectionChanged(PusherState pusherState)
		{
			Logger.WriteInfo("Main", "Connection Status: " + pusherState.Name());
			this.UpdateUI((int)pusherState, null, null);
		}

		private void pusherClient_Connected()
		{
			this.UpdateUI((int)PusherState.Connected, null, null);
		}

		private void pusherClient_Disconnected()
		{
			this.UpdateUI((int)PusherState.Disconnected, null, null);
		}

		private void pusherClient_Error(string message, string stackTrace = null)
		{
			Logger.WriteError("Main", message, stackTrace);
			this.UpdateUI(null, null, "Error: " + message);
		}

		private void pusherChannel_StatusChanged(PusherChannelState channelState)
		{
			Logger.WriteInfo("Main", "Channel Status: " + channelState.Name());
			this.UpdateUI(null, (int)channelState, null);
			if (channelState == PusherChannelState.Subscribed)
			{
				this.pusherChannel.BindAll(this.pusherChannel_BindAll);
			}
			else if (this.pusherChannel != null)
			{
				this.pusherChannel.UnBindAll(this.pusherChannel_BindAll);
			}
		}

		private void pusherChannel_BindAll(string eventName, string eventData)
		{
			Logger.WriteInfo("Main", "Channel Event: " + eventName + ", Data: " + eventData);
		}

		#endregion


		#region ILogger implementation

		private delegate void WriteLineCallback(string msgType, string date, string time, string source, string message, string stackTrace);

		private void WriteToLog(string msgType, string date, string time, string source, string message, string stackTrace)
		{
			// check if being called from a different thread
			if (this.logListView.InvokeRequired)
			{
				this.Invoke(new WriteLineCallback(WriteToLog), new object[] { msgType, date, time, source, message, stackTrace });
			}
			else
			{
				ListViewItem item = new ListViewItem();
				item.ForeColor = (msgType.Equals("Error") ? System.Drawing.Color.Red : System.Drawing.Color.Black);
				item.Text = msgType;
				item.SubItems.Add(date);
				item.SubItems.Add(time);
				item.SubItems.Add(source);
				item.SubItems.Add(message);
				item.SubItems.Add(stackTrace);
				this.logListView.Items.Add(item);
			}
		}

		public void WriteError(string source, string message, string stackTrace = "")
		{
			this.WriteToLog("Error", DateTime.Now.ToString("MM/dd/yyyy"), DateTime.Now.ToString("HH:mm:ss.fff"), source, message, stackTrace);
		}

		public void WriteInfo(string source, string message)
		{
			this.WriteToLog("Info", DateTime.Now.ToString("MM/dd/yyyy"), DateTime.Now.ToString("HH:mm:ss.fff"), source, message, "");
		}

		public void WriteDebug(string source, string message)
		{
			this.WriteToLog("Debug", DateTime.Now.ToString("MM/dd/yyyy"), DateTime.Now.ToString("HH:mm:ss.fff"), source, message, "");
		}

		#endregion
	}
}
